Skip to content

[Dashboards] Register dashboard locator on server start#218495

Merged
nickpeihl merged 15 commits intoelastic:mainfrom
nickpeihl:register-dashboard-locator-server
Apr 28, 2025
Merged

[Dashboards] Register dashboard locator on server start#218495
nickpeihl merged 15 commits intoelastic:mainfrom
nickpeihl:register-dashboard-locator-server

Conversation

@nickpeihl
Copy link
Copy Markdown
Contributor

@nickpeihl nickpeihl commented Apr 16, 2025

Summary

Registers the dashboard locator definition with the share plugin on the Dashboard server start contract.

This moves Dashboard locator code from public to common so that we can expose the Dashboard Locator definition on the server. The getLocation method exposed by the locator does not appear to be used by any server code. So the getDashboardFilterFields function provided to the DashboardLocatorDefinition will throw a not supported error on the server. This should currently never throw since getLocation is not called by the server. But any future implementations where getLocation is called on the server would throw an error and thus would need to augment this function.

For posterity, here's an example where, in the future, we might need to provide a scoped content management client to the DashboardLocatorParams that can be used to load filters from a saved dashboard in the getDashboardFilterFields function.

// in src/platform/plugins/shared/share/server/url_service/http/short_urls/register_create_route.ts

export const registerCreateRoute = (router: IRouter, url: ServerUrlService, contentManagement: ContentManagementServerSetup) => {

router.handleLegacyErrors(async (ctx, req, res) => {
  const savedObjects = (await ctx.core).savedObjects.client;

  const cmClient = contentManagement.contentClient
        .getForRequest({ request: req, requestHandlerContext: ctx })

  const shortUrls = url.shortUrls.get({ savedObjects });
  const { locatorId, params, slug } = req.body;
  const locator = url.locators.get(locatorId);
  // TODO find a way to pass cmClient to the locator. Maybe in params?
  
  try {
  const shortUrl = await shortUrls.create({
    locator,
    params,
    slug,
  });

  return res.ok({
    headers: {
      'content-type': 'application/json',
    },
    body: shortUrl.data,
  });
} catch (error) {

new DashboardAppLocatorDefinition({
useHashedUrl: false,
getDashboardFilterFields: async (dashboardId: string) => {
return [];
Copy link
Copy Markdown
Contributor Author

@nickpeihl nickpeihl Apr 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we want to get the filters from a saved dashboard in the same way we do on the client-side then we'll somehow need to include the RequestHandlerContext so we can scope the content management client to the current user.

Similar to the useHashedUrls example above, we need to use the ctx argument to create a content management client scoped to the user to pass to the locator.

// in src/platform/plugins/shared/share/server/url_service/http/short_urls/register_create_route.ts

export const registerCreateRoute = (router: IRouter, url: ServerUrlService, contentManagement: ContentManagementServerSetup) => {

...

router.handleLegacyErrors(async (ctx, req, res) => {
  const savedObjects = (await ctx.core).savedObjects.client;

  const cmClient = contentManagement.contentClient
        .getForRequest({ request: req, requestHandlerContext: ctx })

  const shortUrls = url.shortUrls.get({ savedObjects });
  const { locatorId, params, slug } = req.body;
  const locator = url.locators.get(locatorId);
  // TODO find a way to pass cmClient to the locator. Maybe in params?
  
  try {
  const shortUrl = await shortUrls.create({
    locator,
    params,
    slug,
  });

  return res.ok({
    headers: {
      'content-type': 'application/json',
    },
    body: shortUrl.data,
  });
} catch (error) {

Copy link
Copy Markdown
Contributor Author

@nickpeihl nickpeihl Apr 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAICT, the getLocation method from the LocatorPublic interface is never actually called by the server. So, it currently does not matter on the Dashboard server contract what we set for useHashedUrls or the return value of getDashboardFilterFields because, currently, they are never be used by Kibana server. It seems that the getLocation is only currently ever used by the client.

Also, the URLService on the Share server plugin only exposes certain locator methods but they are not supported on the server. So I suspect we can safely assume the getLocation is not directly exposed on the server side URL service and it will likely not get called from the server. Maybe someone from @elastic/appex-sharedux can keep me honest here?

So I think we have two options for registering the Dashboard locator on the server:

  1. Have getDashboardFilterFields always return an empty array. Currently, this function is never called. But any future implementations where getLocation is called on the server would not retrieve any saved filters on a dashboard. So it would not break, but might return unexpected results.
  2. Have getDashboardFilterFields throw an error as not supported on the server. Again, this should currently never throw since getLocation is not called by the server. But any future implementations where getLocation is called on the server would throw an error. cc @ThomThomson

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good find! That will definitely make this simpler. I'd suggest making it throw an error on the serverside - which will make sure we revisit this if we ever need to call getLocation on the server.

if (plugins.share) {
plugins.share.url.locators.create(
new DashboardAppLocatorDefinition({
useHashedUrl: false,
Copy link
Copy Markdown
Contributor Author

@nickpeihl nickpeihl Apr 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we want to support hashed urls on the server the same way we do on the client-side, we'll need the RequestHandlerContext) so we can scope the server-side UISettingsServiceStart.asScopedToClient to retrieve the 'state:storeInSessionStorage' setting as we do in the client.

For example: The short URL create route passes the ctx: RequestHandlerContext. We use this to scope the savedObjects client to the user making the request. To retrieve the 'state:storeInSessionStorage' setting we need to do something like this:

// in src/platform/plugins/shared/share/server/url_service/http/short_urls/register_create_route.ts
router.handleLegacyErrors(async (ctx, req, res) => {
  const savedObjects = (await ctx.core).savedObjects.client;

  const uiSettingsClient = core.uiSettings.asScopedToClient(soClient);
  const useHashedUrl = uiSettingsClient.get('state:storeInSessionStorage');

  const shortUrls = url.shortUrls.get({ savedObjects });
  const { locatorId, params, slug } = req.body;
  const locator = url.locators.get(locatorId);
  // TODO find a way to pass useHashedUrl to the locator. Maybe in params?
  
  try {
  const shortUrl = await shortUrls.create({
    locator,
    params,
    slug,
  });

  return res.ok({
    headers: {
      'content-type': 'application/json',
    },
    body: shortUrl.data,
  });
} catch (error) {

FWIW, the server-side Discover locator does not support hashed URLs.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd say it's safe to not support hashing on the serverside locator for Dashboards.

@nickpeihl nickpeihl added backport:version Backport to applied version labels v9.1.0 v8.19.0 Team:Presentation Presentation Team for Dashboard, Input Controls, and Canvas t// release_note:skip Skip the PR/issue when compiling release notes labels Apr 17, 2025
@nickpeihl nickpeihl marked this pull request as ready for review April 18, 2025 11:54
@nickpeihl nickpeihl requested a review from a team as a code owner April 18, 2025 11:54
@nickpeihl nickpeihl requested review from a team April 18, 2025 11:54
@nickpeihl nickpeihl requested review from a team as code owners April 18, 2025 11:54
@elasticmachine
Copy link
Copy Markdown
Contributor

Pinging @elastic/kibana-presentation (Team:Presentation)

@nickpeihl nickpeihl marked this pull request as draft April 18, 2025 12:45

export const UI_SETTINGS = {
ENABLE_LABS_UI: 'labs:dashboard:enable_ui',
};
Copy link
Copy Markdown
Contributor Author

@nickpeihl nickpeihl Apr 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These top level exports were removed to avoid bloating the bundle size. Aside from types, the imports from common in dashboard/public have been changed to deep imports.

@nickpeihl nickpeihl marked this pull request as ready for review April 21, 2025 16:40
@jloleysens jloleysens removed the request for review from a team April 22, 2025 08:54
Copy link
Copy Markdown
Contributor

@rmyz rmyz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

x-pack/solutions/observability/plugins/inventory/public/hooks/use_detail_view_redirect.ts changes LGTM

@botelastic botelastic Bot added the Feature:Drilldowns Embeddable panel Drilldowns label Apr 22, 2025
Copy link
Copy Markdown
Member

@jgowdyelastic jgowdyelastic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ML changes LGTM

Copy link
Copy Markdown
Contributor

@ThomThomson ThomThomson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes LGTM! Mostly import changes, code only review.

new DashboardAppLocatorDefinition({
useHashedUrl: false,
getDashboardFilterFields: async (dashboardId: string) => {
throw new Error(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for throwing an error here!

@Ikuni17 Ikuni17 removed the request for review from a team April 24, 2025 19:17
@Ikuni17
Copy link
Copy Markdown
Contributor

Ikuni17 commented Apr 24, 2025

Removed Operations as we no longer have code owner changes in this PR.

@nickpeihl nickpeihl enabled auto-merge (squash) April 28, 2025 13:56
@nickpeihl nickpeihl merged commit 8e1a426 into elastic:main Apr 28, 2025
11 checks passed
@kibanamachine
Copy link
Copy Markdown
Contributor

Starting backport for target branches: 8.19

https://github.com/elastic/kibana/actions/runs/14711519816

@elasticmachine
Copy link
Copy Markdown
Contributor

💚 Build Succeeded

Metrics [docs]

Public APIs missing comments

Total count of every public API that lacks a comment. Target amount is 0. Run node scripts/build_api_docs --plugin [yourplugin] --stats comments for more detailed information.

id before after diff
dashboard 92 82 -10

Async chunks

Total size of all lazy-loaded chunks that will be downloaded as the user navigates the app

id before after diff
dashboard 548.0KB 548.1KB +22.0B

Public APIs missing exports

Total count of every type that is part of your API that should be exported but is not. This will cause broken links in the API documentation system. Target amount is 0. Run node scripts/build_api_docs --plugin [yourplugin] --stats exports for more detailed information.

id before after diff
dashboard 11 10 -1

Page load bundle

Size of the bundles that are downloaded on every page load. Target size is below 100kb

id before after diff
dashboard 17.7KB 17.8KB +132.0B
Unknown metric groups

API count

id before after diff
dashboard 96 88 -8

History

@kibanamachine
Copy link
Copy Markdown
Contributor

💔 All backports failed

Status Branch Result
8.19 Backport failed because of merge conflicts

Manual backport

To create the backport manually run:

node scripts/backport --pr 218495

Questions ?

Please refer to the Backport tool documentation

@nickpeihl
Copy link
Copy Markdown
Contributor Author

💚 All backports created successfully

Status Branch Result
8.19

Note: Successful backport PRs will be merged automatically after passing CI.

Questions ?

Please refer to the Backport tool documentation

nickpeihl added a commit to nickpeihl/kibana that referenced this pull request Apr 30, 2025
## Summary

Registers the dashboard locator definition with the share plugin on the
Dashboard server start contract.

This moves Dashboard locator code from public to common so that we can
expose the Dashboard Locator definition on the server. The `getLocation`
method exposed by the locator does not appear to be used by any server
code. So the `getDashboardFilterFields` function provided to the
`DashboardLocatorDefinition` will throw a not supported error on the
server. This should currently never throw since getLocation is not
called by the server. But any future implementations where getLocation
is called on the server would throw an error and thus would need to
augment this function.

For posterity, here's an example where, in the future, we might need to
provide a scoped content management client to the DashboardLocatorParams
that can be used to load filters from a saved dashboard in the
`getDashboardFilterFields` function.

```ts
// in src/platform/plugins/shared/share/server/url_service/http/short_urls/register_create_route.ts

export const registerCreateRoute = (router: IRouter, url: ServerUrlService, contentManagement: ContentManagementServerSetup) => {

router.handleLegacyErrors(async (ctx, req, res) => {
  const savedObjects = (await ctx.core).savedObjects.client;

  const cmClient = contentManagement.contentClient
        .getForRequest({ request: req, requestHandlerContext: ctx })

  const shortUrls = url.shortUrls.get({ savedObjects });
  const { locatorId, params, slug } = req.body;
  const locator = url.locators.get(locatorId);
  // TODO find a way to pass cmClient to the locator. Maybe in params?

  try {
  const shortUrl = await shortUrls.create({
    locator,
    params,
    slug,
  });

  return res.ok({
    headers: {
      'content-type': 'application/json',
    },
    body: shortUrl.data,
  });
} catch (error) {
```

(cherry picked from commit 8e1a426)

# Conflicts:
#	src/platform/plugins/shared/dashboard/public/dashboard_top_nav/dashboard_favorite_button.tsx
#	x-pack/platform/plugins/shared/ml/public/application/jobs/new_job/job_from_dashboard/quick_create_job_base.ts
nickpeihl added a commit that referenced this pull request Apr 30, 2025
…) (#219717)

# Backport

This will backport the following commits from `main` to `8.19`:
- [[Dashboards] Register dashboard locator on server start
(#218495)](#218495)

<!--- Backport version: 9.6.6 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sorenlouv/backport)

<!--BACKPORT [{"author":{"name":"Nick
Peihl","email":"nick.peihl@elastic.co"},"sourceCommit":{"committedDate":"2025-04-28T15:23:18Z","message":"[Dashboards]
Register dashboard locator on server start (#218495)\n\n##
Summary\n\nRegisters the dashboard locator definition with the share
plugin on the\nDashboard server start contract.\n\nThis moves Dashboard
locator code from public to common so that we can\nexpose the Dashboard
Locator definition on the server. The `getLocation`\nmethod exposed by
the locator does not appear to be used by any server\ncode. So the
`getDashboardFilterFields` function provided to
the\n`DashboardLocatorDefinition` will throw a not supported error on
the\nserver. This should currently never throw since getLocation is
not\ncalled by the server. But any future implementations where
getLocation\nis called on the server would throw an error and thus would
need to\naugment this function.\n\nFor posterity, here's an example
where, in the future, we might need to\nprovide a scoped content
management client to the DashboardLocatorParams\nthat can be used to
load filters from a saved dashboard in the\n`getDashboardFilterFields`
function.\n\n```ts\n// in
src/platform/plugins/shared/share/server/url_service/http/short_urls/register_create_route.ts\n\nexport
const registerCreateRoute = (router: IRouter, url: ServerUrlService,
contentManagement: ContentManagementServerSetup) =>
{\n\nrouter.handleLegacyErrors(async (ctx, req, res) => {\n const
savedObjects = (await ctx.core).savedObjects.client;\n\n const cmClient
= contentManagement.contentClient\n .getForRequest({ request: req,
requestHandlerContext: ctx })\n\n const shortUrls = url.shortUrls.get({
savedObjects });\n const { locatorId, params, slug } = req.body;\n const
locator = url.locators.get(locatorId);\n // TODO find a way to pass
cmClient to the locator. Maybe in params?\n \n try {\n const shortUrl =
await shortUrls.create({\n locator,\n params,\n slug,\n });\n\n return
res.ok({\n headers: {\n 'content-type': 'application/json',\n },\n body:
shortUrl.data,\n });\n} catch (error)
{\n```","sha":"8e1a426c25497f332fd516dee46061c900618e96","branchLabelMapping":{"^v9.1.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["Team:Presentation","release_note:skip","Feature:Drilldowns","backport:version","v9.1.0","v8.19.0"],"title":"[Dashboards]
Register dashboard locator on server
start","number":218495,"url":"https://github.com/elastic/kibana/pull/218495","mergeCommit":{"message":"[Dashboards]
Register dashboard locator on server start (#218495)\n\n##
Summary\n\nRegisters the dashboard locator definition with the share
plugin on the\nDashboard server start contract.\n\nThis moves Dashboard
locator code from public to common so that we can\nexpose the Dashboard
Locator definition on the server. The `getLocation`\nmethod exposed by
the locator does not appear to be used by any server\ncode. So the
`getDashboardFilterFields` function provided to
the\n`DashboardLocatorDefinition` will throw a not supported error on
the\nserver. This should currently never throw since getLocation is
not\ncalled by the server. But any future implementations where
getLocation\nis called on the server would throw an error and thus would
need to\naugment this function.\n\nFor posterity, here's an example
where, in the future, we might need to\nprovide a scoped content
management client to the DashboardLocatorParams\nthat can be used to
load filters from a saved dashboard in the\n`getDashboardFilterFields`
function.\n\n```ts\n// in
src/platform/plugins/shared/share/server/url_service/http/short_urls/register_create_route.ts\n\nexport
const registerCreateRoute = (router: IRouter, url: ServerUrlService,
contentManagement: ContentManagementServerSetup) =>
{\n\nrouter.handleLegacyErrors(async (ctx, req, res) => {\n const
savedObjects = (await ctx.core).savedObjects.client;\n\n const cmClient
= contentManagement.contentClient\n .getForRequest({ request: req,
requestHandlerContext: ctx })\n\n const shortUrls = url.shortUrls.get({
savedObjects });\n const { locatorId, params, slug } = req.body;\n const
locator = url.locators.get(locatorId);\n // TODO find a way to pass
cmClient to the locator. Maybe in params?\n \n try {\n const shortUrl =
await shortUrls.create({\n locator,\n params,\n slug,\n });\n\n return
res.ok({\n headers: {\n 'content-type': 'application/json',\n },\n body:
shortUrl.data,\n });\n} catch (error)
{\n```","sha":"8e1a426c25497f332fd516dee46061c900618e96"}},"sourceBranch":"main","suggestedTargetBranches":["8.19"],"targetPullRequestStates":[{"branch":"main","label":"v9.1.0","branchLabelMappingKey":"^v9.1.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/218495","number":218495,"mergeCommit":{"message":"[Dashboards]
Register dashboard locator on server start (#218495)\n\n##
Summary\n\nRegisters the dashboard locator definition with the share
plugin on the\nDashboard server start contract.\n\nThis moves Dashboard
locator code from public to common so that we can\nexpose the Dashboard
Locator definition on the server. The `getLocation`\nmethod exposed by
the locator does not appear to be used by any server\ncode. So the
`getDashboardFilterFields` function provided to
the\n`DashboardLocatorDefinition` will throw a not supported error on
the\nserver. This should currently never throw since getLocation is
not\ncalled by the server. But any future implementations where
getLocation\nis called on the server would throw an error and thus would
need to\naugment this function.\n\nFor posterity, here's an example
where, in the future, we might need to\nprovide a scoped content
management client to the DashboardLocatorParams\nthat can be used to
load filters from a saved dashboard in the\n`getDashboardFilterFields`
function.\n\n```ts\n// in
src/platform/plugins/shared/share/server/url_service/http/short_urls/register_create_route.ts\n\nexport
const registerCreateRoute = (router: IRouter, url: ServerUrlService,
contentManagement: ContentManagementServerSetup) =>
{\n\nrouter.handleLegacyErrors(async (ctx, req, res) => {\n const
savedObjects = (await ctx.core).savedObjects.client;\n\n const cmClient
= contentManagement.contentClient\n .getForRequest({ request: req,
requestHandlerContext: ctx })\n\n const shortUrls = url.shortUrls.get({
savedObjects });\n const { locatorId, params, slug } = req.body;\n const
locator = url.locators.get(locatorId);\n // TODO find a way to pass
cmClient to the locator. Maybe in params?\n \n try {\n const shortUrl =
await shortUrls.create({\n locator,\n params,\n slug,\n });\n\n return
res.ok({\n headers: {\n 'content-type': 'application/json',\n },\n body:
shortUrl.data,\n });\n} catch (error)
{\n```","sha":"8e1a426c25497f332fd516dee46061c900618e96"}},{"branch":"8.19","label":"v8.19.0","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"}]}]
BACKPORT-->
akowalska622 pushed a commit to akowalska622/kibana that referenced this pull request May 29, 2025
## Summary

Registers the dashboard locator definition with the share plugin on the
Dashboard server start contract.

This moves Dashboard locator code from public to common so that we can
expose the Dashboard Locator definition on the server. The `getLocation`
method exposed by the locator does not appear to be used by any server
code. So the `getDashboardFilterFields` function provided to the
`DashboardLocatorDefinition` will throw a not supported error on the
server. This should currently never throw since getLocation is not
called by the server. But any future implementations where getLocation
is called on the server would throw an error and thus would need to
augment this function.

For posterity, here's an example where, in the future, we might need to
provide a scoped content management client to the DashboardLocatorParams
that can be used to load filters from a saved dashboard in the
`getDashboardFilterFields` function.

```ts
// in src/platform/plugins/shared/share/server/url_service/http/short_urls/register_create_route.ts

export const registerCreateRoute = (router: IRouter, url: ServerUrlService, contentManagement: ContentManagementServerSetup) => {

router.handleLegacyErrors(async (ctx, req, res) => {
  const savedObjects = (await ctx.core).savedObjects.client;

  const cmClient = contentManagement.contentClient
        .getForRequest({ request: req, requestHandlerContext: ctx })

  const shortUrls = url.shortUrls.get({ savedObjects });
  const { locatorId, params, slug } = req.body;
  const locator = url.locators.get(locatorId);
  // TODO find a way to pass cmClient to the locator. Maybe in params?
  
  try {
  const shortUrl = await shortUrls.create({
    locator,
    params,
    slug,
  });

  return res.ok({
    headers: {
      'content-type': 'application/json',
    },
    body: shortUrl.data,
  });
} catch (error) {
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport:version Backport to applied version labels Feature:Drilldowns Embeddable panel Drilldowns release_note:skip Skip the PR/issue when compiling release notes Team:Presentation Presentation Team for Dashboard, Input Controls, and Canvas t// v8.19.0 v9.1.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants